From 339bc1ef13676c73c6c8b0b97fa1f5ca8cf4dd35 Mon Sep 17 00:00:00 2001 From: "kaf24@firebug.cl.cam.ac.uk" Date: Fri, 5 Aug 2005 14:57:43 +0000 Subject: [PATCH] Fix xtime_lock handling to avoid deadlock in sched_clock(). Signed-off-by: Keir Fraser --- .../arch/xen/i386/kernel/time.c | 58 ++++++++++--------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/linux-2.6-xen-sparse/arch/xen/i386/kernel/time.c b/linux-2.6-xen-sparse/arch/xen/i386/kernel/time.c index fe732b2809..cc8098f2e2 100644 --- a/linux-2.6-xen-sparse/arch/xen/i386/kernel/time.c +++ b/linux-2.6-xen-sparse/arch/xen/i386/kernel/time.c @@ -233,6 +233,10 @@ static void update_wallclock(void) time_t wtm_sec, xtime_sec; u64 tmp, usec; + if ((shadow_tv.tv_sec == s->wc_sec) && + (shadow_tv.tv_usec == s->wc_usec)) + return; + shadow_tv.tv_sec = s->wc_sec; shadow_tv.tv_usec = s->wc_usec; @@ -263,9 +267,9 @@ static void update_wallclock(void) /* * Reads a consistent set of time-base values from Xen, into a shadow data - * area. Must be called with the xtime_lock held for writing. + * area. */ -static void __get_time_values_from_xen(void) +static void get_time_values_from_xen(void) { shared_info_t *s = HYPERVISOR_shared_info; struct vcpu_time_info *src; @@ -286,10 +290,6 @@ static void __get_time_values_from_xen(void) while (dst->version != src->time_version1); dst->tsc_to_usec_mul = dst->tsc_to_nsec_mul / 1000; - - if ((shadow_tv.tv_sec != s->wc_sec) || - (shadow_tv.tv_usec != s->wc_usec)) - update_wallclock(); } static inline int time_values_up_to_date(int cpu) @@ -341,10 +341,10 @@ void do_gettimeofday(struct timeval *tv) unsigned long seq; unsigned long usec, sec; unsigned long max_ntp_tick; - unsigned long flags; s64 nsec; unsigned int cpu; struct shadow_time_info *shadow; + u32 local_time_version; cpu = get_cpu(); shadow = &per_cpu(shadow_time, cpu); @@ -352,6 +352,7 @@ void do_gettimeofday(struct timeval *tv) do { unsigned long lost; + local_time_version = shadow->version; seq = read_seqbegin(&xtime_lock); usec = get_usec_offset(shadow); @@ -387,12 +388,11 @@ void do_gettimeofday(struct timeval *tv) * overflowed). Detect that and recalculate * with fresh values. */ - write_seqlock_irqsave(&xtime_lock, flags); - __get_time_values_from_xen(); - write_sequnlock_irqrestore(&xtime_lock, flags); + get_time_values_from_xen(); continue; } - } while (read_seqretry(&xtime_lock, seq)); + } while (read_seqretry(&xtime_lock, seq) || + (local_time_version != shadow->version)); put_cpu(); @@ -435,7 +435,7 @@ int do_settimeofday(struct timespec *tv) again: nsec = (s64)tv->tv_nsec - (s64)get_nsec_offset(shadow); if (unlikely(!time_values_up_to_date(cpu))) { - __get_time_values_from_xen(); + get_time_values_from_xen(); goto again; } @@ -517,21 +517,21 @@ unsigned long long monotonic_clock(void) { int cpu = get_cpu(); struct shadow_time_info *shadow = &per_cpu(shadow_time, cpu); - s64 off; - unsigned long flags; - - for ( ; ; ) { - off = get_nsec_offset(shadow); - if (time_values_up_to_date(cpu)) - break; - write_seqlock_irqsave(&xtime_lock, flags); - __get_time_values_from_xen(); - write_sequnlock_irqrestore(&xtime_lock, flags); - } + u64 time; + u32 local_time_version; + + do { + local_time_version = shadow->version; + smp_rmb(); + time = shadow->system_timestamp + get_nsec_offset(shadow); + if (!time_values_up_to_date(cpu)) + get_time_values_from_xen(); + smp_rmb(); + } while (local_time_version != shadow->version); put_cpu(); - return shadow->system_timestamp + off; + return time; } EXPORT_SYMBOL(monotonic_clock); @@ -565,7 +565,7 @@ static inline void do_timer_interrupt(int irq, void *dev_id, struct shadow_time_info *shadow = &per_cpu(shadow_time, cpu); do { - __get_time_values_from_xen(); + get_time_values_from_xen(); delta = delta_cpu = shadow->system_timestamp + get_nsec_offset(shadow); @@ -602,6 +602,8 @@ static inline void do_timer_interrupt(int irq, void *dev_id, update_process_times(user_mode(regs)); profile_tick(CPU_PROFILING, regs); } + + update_wallclock(); } /* @@ -788,7 +790,8 @@ void __init time_init(void) return; } #endif - __get_time_values_from_xen(); + get_time_values_from_xen(); + update_wallclock(); xtime.tv_sec = shadow_tv.tv_sec; xtime.tv_nsec = shadow_tv.tv_usec * NSEC_PER_USEC; set_normalized_timespec(&wall_to_monotonic, @@ -872,7 +875,8 @@ void time_resume(void) init_cpu_khz(); /* Get timebases for new environment. */ - __get_time_values_from_xen(); + get_time_values_from_xen(); + update_wallclock(); /* Reset our own concept of passage of system time. */ processed_system_time = -- 2.30.2